#include <stdio.h>
#include <string.h>
#include "i2c.h"
#include "esp_log.h"


#define I2C_BUF_SIZE 128

typedef struct
{
    int    isInit;
    int    sda;
    int    scl;
    int    clk;
    I2CDev dev;
} I2CObj;

typedef struct
{
    I2CObj     i2c[SOC_I2C_NUM];
    const char TAG[10];
} I2CManager;

static esp_err_t  I2C_masterReadSlave(I2CPacket packet);
static esp_err_t  I2C_masterWriteSlave(I2CPacket packet);
static esp_err_t  I2C_masterReadSlaveFromCmd(i2c_port_t i2cNum, i2c_cmd_handle_t cmd);

static I2CManager sgManager = {
    .TAG = "i2c",
};

#define I2C_ERROR(format, ...) ESP_LOGE(sgManager.TAG, format, ##__VA_ARGS__)
#define I2C_WARN(format, ...)  ESP_LOGW(sgManager.TAG, format, ##__VA_ARGS__)
#define I2C_INFO(format, ...)  ESP_LOGI(sgManager.TAG, format, ##__VA_ARGS__)
#define I2C_DEBUG(format, ...) ESP_LOGD(sgManager.TAG, format, ##__VA_ARGS__)

void I2C_init(void)
{
    for (int i = 0; i < SOC_I2C_NUM; i++)
    {
        sgManager.i2c[i].dev.busNum      = i;
        sgManager.i2c[i].dev.write       = I2C_masterWriteSlave;
        sgManager.i2c[i].dev.read        = I2C_masterReadSlave;
        sgManager.i2c[i].dev.readFromCmd = I2C_masterReadSlaveFromCmd;
    }
}

static esp_err_t I2C_createCheckPara(i2c_port_t busNum, int sda, int scl)
{
    if (busNum >= SOC_I2C_NUM)
    {
        return ESP_FAIL;
    }

    if (PIN_getState(sda) || PIN_getState(scl))
    {
        return ESP_FAIL;
    }

    return ESP_OK;
}

void I2C_delDev(i2c_port_t busNum)
{
    i2c_driver_delete(busNum);

    PIN_release(sgManager.i2c[busNum].sda);
    PIN_release(sgManager.i2c[busNum].scl);

    sgManager.i2c[busNum].isInit = 0;
    sgManager.i2c[busNum].clk    = 0;
    sgManager.i2c[busNum].sda    = 0;
    sgManager.i2c[busNum].scl    = 0;
}

esp_err_t I2C_createDev(i2c_port_t busNum, int sda, int scl, int clk, I2CDev **dev)
{
    esp_err_t    err  = ESP_OK;
    i2c_config_t conf = {0};


    if (I2C_createCheckPara(busNum, sda, scl))
    {
        I2C_ERROR("para error\n");
        return ESP_FAIL;
    }

    PIN_register(sda, PIN_SDA);
    PIN_register(scl, PIN_SCL);

    conf.mode                 = I2C_MODE_MASTER;
    conf.sda_io_num           = sda;
    conf.sda_pullup_en        = GPIO_PULLUP_ENABLE;
    conf.scl_io_num           = scl;
    conf.scl_pullup_en        = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed     = clk;

    sgManager.i2c[busNum].clk = clk;
    sgManager.i2c[busNum].sda = sda;
    sgManager.i2c[busNum].scl = scl;

    /*i2c配置*/
    err = i2c_param_config(busNum, &conf);
    if (err != ESP_OK)
    {
        I2C_ERROR("i2c config error\n");
        goto free;
    }

    err = i2c_driver_install(busNum, conf.mode, 0, 0, 0);
    if (err != ESP_OK)
    {
        I2C_ERROR("i2c config error\n");
        goto free;
    }

    sgManager.i2c[busNum].isInit = 1;
    /*输出dev*/
    *dev = &sgManager.i2c[busNum].dev;

    return ESP_OK;

free:
    I2C_delDev(busNum);
    return err;
}

esp_err_t I2C_getDev(int busNum, int sda, int scl, int clk, I2CDev **dev)
{
    if (busNum >= SOC_I2C_NUM || !sgManager.i2c[busNum].isInit)
    {
        I2C_ERROR("i2c[%d] is invalid\n", busNum);
        return ESP_FAIL;
    }

    if (sda != sgManager.i2c[busNum].sda || scl != sgManager.i2c[busNum].scl || clk != sgManager.i2c[busNum].clk)
    {
        I2C_ERROR("i2c[%d] para is not match\n", busNum);
        return ESP_FAIL;
    }

    *dev = &sgManager.i2c[busNum].dev;
    return ESP_OK;
}

static esp_err_t I2C_masterReadSlaveFromCmd(i2c_port_t i2cNum, i2c_cmd_handle_t cmd)
{
    return i2c_master_cmd_begin(i2cNum, cmd, 10 / portTICK_PERIOD_MS);
}

static esp_err_t I2C_masterReadSlave(I2CPacket packet)
{
    esp_err_t  ret    = ESP_OK;
    i2c_port_t i2cNum = packet.i2cNum;
    int        addr   = packet.addr;
    uint8_t   *dataRd = (uint8_t *)packet.buf;
    size_t     size   = packet.size;

    if (size == 0)
    {
        return ESP_OK;
    }
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, 1);
    if (size > 1)
    {
        i2c_master_read(cmd, dataRd, size - 1, I2C_MASTER_ACK);
    }
    i2c_master_read(cmd, dataRd + size - 1, 1, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2cNum, cmd, 10 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    return ret;
}

static esp_err_t I2C_masterWriteSlave(I2CPacket packet)
{
    esp_err_t        ret     = ESP_OK;
    i2c_port_t       i2cNum  = packet.i2cNum;
    int              addr    = packet.addr;
    uint8_t         *data_wr = (uint8_t *)packet.buf;
    size_t           size    = packet.size;


    i2c_cmd_handle_t cmd     = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, 1);
    i2c_master_write(cmd, data_wr, size, 1);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2cNum, cmd, 10 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}